/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

using System;
using System.Collections.Generic;
using System.Diagnostics;
using Lucene.Net.Index;
using Lucene.Net.Search;

namespace Lucene.Net.Spatial.Util
{
    /// <summary>
    /// Constructs a filter for docs matching any of the terms added to this class.
    /// Unlike a RangeFilter this can be used for filtering on multiple terms that are not necessarily in
    /// a sequence. An example might be a collection of primary keys from a database query result or perhaps
    /// a choice of "category" labels picked by the end user. As a filter, this is much faster than the
    /// equivalent query (a BooleanQuery with many "should" TermQueries)
    /// </summary>
    public class TermsFilter : Filter
    {
        private readonly SortedSet<Term> terms = new SortedSet<Term>();

        /// <summary>
        /// Adds a term to the list of acceptable terms
        /// </summary>
        /// <param name="term"></param>
        public void AddTerm(Term term)
        {
            terms.Add(term);
        }

        public override DocIdSet GetDocIdSet(IndexReader reader)
        {
            var result = new FixedBitSet(reader.MaxDoc);
            var fields = reader.GetFieldNames(IndexReader.FieldOption.ALL);

            if (fields == null || fields.Count == 0)
            {
                return result;
            }

            String lastField = null;
            TermsEnumCompatibility termsEnum = null;
            foreach (Term term in terms)
            {
                if (!term.Field.Equals(lastField))
                {
                    var termsC = new TermsEnumCompatibility(reader, term.Field);
                    if (termsC.Term() == null)
                    {
                        return result;
                    }
                    termsEnum = termsC;
                    lastField = term.Field;
                }

                if (terms != null)
                {
                    // TODO this check doesn't make sense, decide which variable its supposed to be for
                    Debug.Assert(termsEnum != null);
                    if (termsEnum.SeekCeil(term.Text) == TermsEnumCompatibility.SeekStatus.FOUND)
                    {
                        termsEnum.Docs(result);
                    }
                }
            }
            return result;
        }

        public override bool Equals(object obj)
        {
            if (this == obj)
                return true;

            if ((obj == null) || (obj.GetType() != this.GetType()))
                return false;

            var test = (TermsFilter)obj;
            if (terms == test.terms)
                return true;
            if (terms == null || terms.Count != test.terms.Count)
                return false;

            var e1 = terms.GetEnumerator();
            var e2 = test.terms.GetEnumerator();
            while (e1.MoveNext() && e2.MoveNext())
            {
                if (!e1.Current.Equals(e2.Current)) return false;
            }
            return true;
        }

        public override int GetHashCode()
        {
            int hash = 9;
            foreach (Term term in terms)
            {
                hash = 31 * hash + term.GetHashCode();
            }
            return hash;
        }
    }
}
